﻿using IOP.OpenType;
using IOP.OpenType.Tables;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace IOP.Codec.OpenType
{
    /// <summary>
    /// CFF1.0加载器
    /// </summary>
    public class CFFLoader
    {
        /// <summary>
        /// 加载
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="entry"></param>
        /// <returns></returns>
        public async Task<CFFTable> Load(FileStream stream, TableEntry entry)
        {
            uint offset = entry.Offset;
            stream.Seek(offset, SeekOrigin.Begin);
            var pipeReader = PipeReader.Create(stream, new StreamPipeReaderOptions(null, (int)entry.Length, 2048, false));
            var r = await ReadFromPipe(pipeReader, entry);
            return r;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private async Task<CFFTable> ReadFromPipe(PipeReader reader, TableEntry entry)
        {
            try
            {
                CFFTable table = new CFFTable();
                table.Entry = entry;
                long byteIndex = 0;
                ReadResult result = await reader.ReadAsync();
                ReadOnlySequence<byte> buffer = result.Buffer;
                ReadCFFHeader(ref buffer, ref byteIndex, table);
                ReadNameIndex(ref buffer, ref byteIndex, table);
                return table;
            }
            catch
            {
                throw;
            }
            finally
            {
                reader.Complete();
            }
        }

        /// <summary>
        /// 读取表头
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="byteIndex"></param>
        /// <param name="table"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void ReadCFFHeader(ref ReadOnlySequence<byte> buffer, ref long byteIndex, CFFTable table)
        {
            SequenceReader<byte> reader = new SequenceReader<byte>(buffer);
            reader.TryRead(out byte major);
            reader.TryRead(out byte minor);
            reader.TryRead(out byte hdrSize);
            reader.TryRead(out byte offSize);
            CFFTableHeader header = new CFFTableHeader(major, minor, hdrSize, offSize);
            table.Header = header;
            byteIndex += 4;
        }
        /// <summary>
        /// 读取名称索引
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="byteIndex"></param>
        /// <param name="table"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void ReadNameIndex(ref ReadOnlySequence<byte> buffer, ref long byteIndex, CFFTable table)
        {
            INDEXData[] indexes = ReadIndexData(ref buffer, ref byteIndex);
            if (indexes == null || indexes.Length <= 0) return;
            string[] names = new string[indexes.Length];
            SequenceReader<byte> reader = new SequenceReader<byte>(buffer);
            reader.Advance(byteIndex);
            for(int i = 0; i < indexes.Length; ++i)
            {
                string s = ReadString(ref reader, indexes[i].Length);
                names[i] = s;
            }
            table.NameIndexes = names;
            byteIndex = reader.Consumed;
        }
        /// <summary>
        /// 读取顶层DICT数据索引
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="byteIndex"></param>
        /// <param name="table"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void ReadTopDICIndex(ref ReadOnlySequence<byte> buffer, ref long byteIndex, CFFTable table)
        {
            //8. Top DICT INDEX
            //This contains the top - level DICTs of all the fonts in the FontSet
            //stored in an INDEX structure.Objects contained within this
            //INDEX correspond to those in the Name INDEX in both order
            //and number. Each object is a DICT structure that corresponds to
            //the top-level dictionary of a PostScript font.
            //A font is identified by an entry in the Name INDEX and its data
            //is accessed via the corresponding Top DICT
            INDEXData[] indexes = ReadIndexData(ref buffer, ref byteIndex);

            //9. Top DICT Data
            //The names of the Top DICT operators shown in 
            //Table 9 are, where possible, the same as the corresponding Type 1 dict key. 
            //Operators that have no corresponding Type1 dict key are noted 
            //in the table below along with a default value, if any. (Several
            //operators have been derived from FontInfo dict keys but have
            //been grouped together with the Top DICT operators for
            //simplicity.The keys from the FontInfo dict are indicated in the
            //Default, notes  column of Table 9)
            int count = indexes.Length;
            if (count > 1) throw new NotSupportedException();
            for(int i = 0; i < count; i++)
            {
                var topDic = ReadDICTData(ref buffer, ref byteIndex, indexes[i].Length + byteIndex);
                table.TopLevelIndex = topDic[0];
            }
        }

        /// <summary>
        /// 读取字符串
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.NoInlining)]
        private string ReadString(ref SequenceReader<byte> reader, int length)
        {
            Span<byte> local = length > 256 * 1024 ? new byte[length] : stackalloc byte[length];
            reader.TryCopyTo(local);
            reader.Advance(length);
            return Encoding.UTF8.GetString(local);
        }
        /// <summary>
        /// 读取数值类型
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="byteIndex"></param>
        /// <param name="b0"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private int ReadIntData(ref SequenceReader<byte> reader, ref long byteIndex, byte b0)
        {
            int r;
            if (b0 == 28)
            {
                reader.TryReadBigEndian(out short v); 
                byteIndex += sizeof(short);
                r = v;
            }
            else if (b0 == 29)
            {
                reader.TryReadBigEndian(out r); 
                byteIndex += sizeof(int);
            }
            else if (b0 >= 32 && b0 <= 246)
            {
                r = b0 - 139;
            }
            else if (b0 >= 247 && b0 <= 250)
            {
                reader.TryRead(out byte b1); 
                byteIndex++;
                r = (b0 - 247) * 256 + b1 + 108;
            }
            else if (b0 >= 251 && b0 <= 254)
            {
                reader.TryRead(out byte b1); 
                byteIndex++;
                r = -(b0 - 251) * 256 - b1 - 108;
            }
            else throw new NotSupportedException("Read integer data failed, Unknown integer data type");
            return r;
        }
        /// <summary>
        /// 读取实数
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="byteIndex"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private double ReadRealData(ref SequenceReader<byte> reader, ref long byteIndex)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 读取单个DICT数据
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="byteIndex"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private DICTData ReadSingleDICTData(ref ReadOnlySequence<byte> buffer, ref long byteIndex, long end)
        {
            SequenceReader<byte> reader = new SequenceReader<byte>(buffer);
            reader.Advance(byteIndex);
            DICTData r = null;
            CFFOperator @operator;
            List<CFFOperand> operands = new List<CFFOperand>();
            while (byteIndex < end)
            {
                reader.TryRead(out byte b0); byteIndex++;
                if (b0 >= 0 && b0 <= 21)
                {
                    if(b0 == 12)
                    {
                        reader.TryRead(out byte b1); byteIndex++;
                        if (CFFOperator.TryGetOperator(b0, b1, out @operator))
                        {
                            r = new DICTData() { Operands = operands.ToArray(), Operator = @operator };
                            break;
                        }
                        else break;
                    }
                    else
                    {
                        if (CFFOperator.TryGetOperator(b0, 0, out @operator))
                        {
                            r = new DICTData() { Operands = operands.ToArray(), Operator = @operator };
                            break;
                        }
                        else break;
                    }
                }
                else if (b0 == 28 || b0 == 29)
                {
                    int v = ReadIntData(ref reader, ref byteIndex, b0);
                    operands.Add(v);
                }
                else if (b0 == 30)
                {

                }
            }
            return r;
        }

        /// <summary>
        /// 读取索引数据
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="byteIndex"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private INDEXData[] ReadIndexData(ref ReadOnlySequence<byte> buffer, ref long byteIndex)
        {
            SequenceReader<byte> reader = new SequenceReader<byte>(buffer);
            reader.Advance(byteIndex);
            reader.TryReadBigEndian(out short c);
            ushort count = (ushort)c;
            INDEXData[] result = new INDEXData[count];
            if (count <= 0) return result;
            reader.TryRead(out byte o);
            int first = 0;
            for(int i = 0; i <= count; i++)
            {
                int offset;
                switch (o)
                {
                    default: throw new NotSupportedException();
                    case 1:
                        reader.TryRead(out byte b);
                        offset = b;
                        break;
                    case 2:
                        reader.TryReadBigEndian(out short s);
                        offset = s;
                        break;
                    case 3:
                        reader.TryRead(out byte f);
                        reader.TryRead(out byte sc);
                        reader.TryRead(out byte t);
                        offset = (f << 16) | (sc << 8) | t;
                        break;
                    case 4:
                        reader.TryReadBigEndian(out int ii);
                        offset = ii;
                        break;
                }
                INDEXData inData = new INDEXData(first, offset - first);
                first = offset;
                if (i - 1 >= 0) result[i - 1] = inData;
            }
            byteIndex = reader.Consumed;
            return result;
        }
        /// <summary>
        /// 读取DICT数据
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="byteIndex"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private List<DICTData> ReadDICTData(ref ReadOnlySequence<byte> buffer, ref long byteIndex, long end)
        {
            SequenceReader<byte> reader = new SequenceReader<byte>(buffer);
            reader.Advance(byteIndex);
            throw new NotImplementedException();
        }
    }
}
